package com.cos.shiro.realm;

import com.cos.entity.SysRole;
import com.cos.entity.SysUser;
import com.cos.services.SysRoleServices;
import com.cos.services.SysUserServices;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author: COS
 * @time: 2021/11/22 16:33
 * @description:
 */
//Component("jdbcRealm")
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private SysUserServices userServices;
    @Autowired
    private SysRoleServices roleServices;
    /**
     *
     * @author: COS
     * @return:
     * @time: 2021/11/23 9:45
     * @description: 认证的
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1. 自定义Realm的方法，从数据库获取对应的记录，返回给shiro继承org.apache.shiro.realm.AuthenticatingRealm类实现doGetAuthenticationInfo(AuthenticationToken)方法
        //2. 把AuthenticationInfo的token转换为 UsernamePasswordToken
//        System.out.println(token.hashCode());
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //3. 从UsernamePasswordToken获取username
        String username = usernamePasswordToken.getUsername();
        //4. 调用数据库方法返回对象，查询username是否存在
        SysUser user = userServices.getUserByName(username);
        //5. 如用户不存在，抛出UnknownAccountException异常,以及根据用户信息的情况，决定是否抛出其他异常,如:没有授权UnauthorizedException,被锁定LockedAccountException
        if (user == null) {
            throw new UnknownAccountException("用户账户不存在");
        }
        //6. 创建String 获取realmName,即当前Realm对象的name ，调用父类的getName()即可
        //7. 根据用户的情况来构建AuthenticationInfo并返回,它是个接口，一般使用它的实现类SimpleAuthenticationInfo,由shiro完成对密码的比对
        //        - 注意要数据库查询出的对象 new SimpleAuthenticationInfo( principal,credentials,realmName)
        //        - Object principal 身份信息，即用户名,也可以是数据表对应的实体类对象
        //        - Object credentials 密码 注意是从数据库中获取到的密码！！shiro会自动帮我们进行密码验证
        //        - String realmName  当前Realm对象的name ，调用父类的getName()即可
        String readlName = getName();
        //设置盐值,使用账户的用户名作为生成盐值使用
        ByteSource salt = ByteSource.Util.bytes(user.getUsername());
        AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),salt,readlName);
        //8. 返回AuthenticationInfo对象
        //9. 记得在filterChainDefinitions配置访问权限放行登录操作
        return info;
    }
    /**
     *
     * @author: COS
     * @return:
     * @time: 2021/11/23 9:45
     * @description: 授权的事情
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //1从PrincipalCollection的principals.getPrimaryPrincipal()中获取登录用户的信息
        Object primaryPrincipal = principals.getPrimaryPrincipal();
        //2利用登录用户信息来获取当前用户的角色或者权限,模拟实现
        System.out.println("进入授权");
//        System.out.println(primaryPrincipal);
        List<SysRole> userRole = roleServices.getUserRoleByName(primaryPrincipal.toString());
        //2.1 登录信息有包含
        //2.2需要从数据库中查询登录用户的权限,创建set<String>集合保存权限
        Set<String> roles = new HashSet<>();
        //2.3不管是那个用户只要能登录都基于user权限roles.add("user")
        for (SysRole sysRole : userRole) {
            roles.add(sysRole.getName());
        }
        //2.4用户名为admin的给予admin以及user的权限,即principal名字是admin,在添加admin权限进set
        //3创建SimpleAuthorizationInfo并设置其Reles属性
        System.out.println(roles);
        SimpleAuthorizationInfo info =new SimpleAuthorizationInfo(roles);
        //6返回SimpleAuthorizationInfo对象
        return info;
    }
}
